package com.sinai.mshab.client.ui;

import java.util.ArrayList;
import java.util.Date;

import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FocusListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.sinai.mshab.client.util.Answer;
import com.sinai.mshab.client.ui.aggregate.DataReceiver;
import com.sinai.mshab.client.ui.aggregate.FormData;
import com.sinai.mshab.client.ui.aggregate.Page;
import com.sinai.mshab.client.ui.basic.Mask;
import com.sinai.mshab.client.ui.validate.Validator;

/**
 * This is the top level class of all widget components in MSHAB, all MSHAB
 * widgets extend this class.
 * 
 * FormComponent STORES the following: the widget itself, the table name and
 * column this widgets information should be stored in, the description label,
 * quantifier label, and error message associated with the widget, the widget
 * mask (eg. phone number), the widget validator (eg. email validator), the
 * current Page that this widget is appended to.
 * 
 * FormComponent AUTOMATES the following: submitting - all widgets can send
 * their answers to a class implementing DataReceiver (eg. FormData), from this
 * answer the server will automatically create an SQL statement and store this
 * widget's value in the database. Populating - all widget can be populated with
 * the value retreived from the database in it's specified table name and
 * column. Cloning - all widgets can be cloned, aggregate widgets clone
 * themselves by asking their smaller components to clone themselves. OBSERVER
 * PATTERN - all widgets can pass messages to their parents or other widgets who
 * sign up as observers of this widget.
 * 
 * @author serge
 * 
 */
public abstract class FormComponent extends Composite implements FocusListener {

	/** The receiver of this widget's data */
	private DataReceiver data_receiver = FormData.getInstance();

	/** Remember if this is a subquestion */
	protected boolean subquestion = false;

	/** This arraylist stores all the observers of this widget */
	protected ArrayList observers = new ArrayList();

	/**
	 * Stores additional values for other fields that go to the table.
	 */
	protected Answer hiddenFields = new Answer();

	/**
	 * This variable corresponds to the field in the database where the answer
	 * from this widget will be stored in.
	 */
	protected String dbfieldName;

	/**
	 * The table name of this <code>FormComponent</code>.
	 */
	protected String tableName;

	/**
	 * The description {@link Label} that appears in front of this
	 * <code>FormComponent</code>.
	 */
	protected String description;

	/**
	 * The quantifier {@link Label} that appears after this
	 * <code>FormComponent</code>.
	 */
	protected String quantifier;

	/**
	 * The error message {@link Label} that appears when a
	 * <code>FormComponent</code> is in an error state and appears after the
	 * quantifier.
	 */
	protected String error_msg;

	/**
	 * The validator that can potentially be used with this widget.
	 */
	protected Validator validator;

	/**
	 * The mask that can potentially be applied to this field.
	 */
	protected Mask mask;

	/**
	 * Indicates whether this <code>FormComponent</code> is required.
	 */
	private boolean required;

	/**
	 * Unless otherwise specified, the id of each form component is the unix
	 * time.
	 */
	private double id = newId();

	/** Specified which page this widget is on */
	private Page myPage;

	abstract public void sendAnswer(DataReceiver receiver);

	/**
	 * Creates a new id for this component
	 * 
	 * @return a new id of this component
	 */
	public double newId() {
		long time = (new Date().getTime());
		String time_s = time + "";
		double final_id = Double.parseDouble(time_s.substring(7));
		return final_id;
	}

	/**
	 * The method to return this widget's answer. Only there for basic widgets,
	 * aggregate widgets would not return answers.
	 * 
	 * @return the answer of this widget.
	 */
	public Answer getAnswer() {
		return null;
	}

	/**
	 * Sets the answer of this <code>FormComponent</code> to
	 * <code>answer</code>.
	 * 
	 * @return TODO
	 */
	public boolean setAnswer(String answer) {
		return true;
	}

	public boolean validate() {
		return false;
	}

	/**
	 * Method to set the validator for all widgets
	 * 
	 * @param v
	 *            the validator to be used.
	 */
	public void setValidator(Validator v) {
		validator = v;
	}

	/**
	 * Returns the validator for this widget.
	 * 
	 * @return the validator being used.
	 */
	public Validator getValidator() {
		return validator;
	}

	/**
	 * Method to set the mask for all widgets
	 * 
	 * @param m
	 *            the mask to be used.
	 */
	public void setMask(Mask m) {
		mask = m;
	}

	/**
	 * Adds a widget to the list of all widgets who are watching this widget for
	 * changes.
	 * 
	 * @param o
	 *            the new observer.
	 */
	public void addObserver(FormComponent o) {
		observers.add(o);
	}

	/**
	 * As an observer, this widget can be notified with a message.
	 * 
	 * @param event
	 *            the message being passed by the observable.
	 */
	public void receiveNotification(FormComponent who) {

	}

	/**
	 * Notifies all observers of an event.
	 * 
	 * @param event
	 *            the information to send to the observer
	 */
	public void notifyObservers() {
		for (int i = 0; i < observers.size(); i++) {
			((FormComponent) observers.get(i)).receiveNotification(this);
		}
	}

	/**
	 * Overrride this if neccessary
	 */
	public void onFocus(Widget sender) {

	}

	/**
	 * Overrride this if neccessary
	 */
	public void onLostFocus(Widget sender) {

	}

	/**
	 * Returns the field name of this widget
	 * 
	 * @return Returns the field name of this widget
	 */
	public String getDbfieldName() {
		return dbfieldName;
	}

	/**
	 * Add new values to hidden fields.
	 * 
	 * @param fieldName
	 *            the field name
	 * @param tableName
	 *            the name of the database table where the textbox information
	 *            will be stored.
	 * @param val
	 *            the value of the field
	 */
	public void addFieldValues(String fieldName, String val) {
		hiddenFields.add(fieldName, "", val);
	}

	/**
	 * Sets this question as a subquestion To be over-written by individual
	 * components
	 */
	public void setAsSubQuestion() {
		subquestion = true;
	}

	/**
	 * Adds a {@link ClickListener} to this <code>FormComponent</code>.
	 * 
	 * @param listener
	 *            the <code>ClickListener</code> to add.
	 */
	public void addClickListener(ClickListener listener) {

	}

	/**
	 * Enables this <code>FormComponent</code> if <code>enabled</code> is
	 * true and disables it otherwise.
	 * 
	 * @param enabled
	 *            determines whether to enable or disable this
	 *            <code>FormComponent</code>
	 */
	public void setEnabled(boolean enabled) {

	}

	public abstract Object clone();

	/**
	 * Sets the new id to this form component
	 * 
	 * @param id
	 *            the new id
	 */
	public void setId(double id) {
		this.id = id;
	}

	/**
	 * Retrieves the id of this component
	 * 
	 * @return the id of this component
	 */
	public double getId() {
		return id;
	}

	/**
	 * Returns true if this <code>FormComponent</code> is required and false
	 * otherwise.
	 * 
	 * @return true if this <code>FormComponent</code> is required and false
	 *         otherwise.
	 */
	public boolean isRequired() {
		return required;
	}

	/**
	 * <code>required</code> indicates whether this <code>FormComponent</code>
	 * is required or not.
	 * 
	 * @param required
	 *            true if this <code>FormComponent</code> is required and
	 *            false otherwise.
	 */
	public void setRequired(boolean required) {
		this.required = required;
	}

	/**
	 * Tells this widget to populate itself using a table name
	 * 
	 * @return TODO
	 */
	public boolean populate() {
		return true;
	}

	/**
	 * Tells this widget to populate itself using an answer object
	 * 
	 * @param answer
	 *            the answer object containing data for this widget.
	 * @return TODO
	 */
	public boolean populate(Answer answer) {
		return true;
	}

	public Page getPage() {
		return myPage;
	}

	/**
	 * @param data_receiver
	 *            the data_receiver to set
	 */
	public void setData_receiver(DataReceiver data_receiver) {
		this.data_receiver = data_receiver;
	}

	/**
	 * @return the data_receiver
	 */
	public DataReceiver getData_receiver() {
		return data_receiver;
	}

	/**
	 * Returns the name of the database table associated with this
	 * <code>FormComponent</code>.
	 * 
	 * @return the database table name of this component.
	 */
	public String getTableName() {
		return tableName;
	}

	/**
	 * Set the database table name of this Form<code>FormComponent</code> to
	 * <code>tableName</code>.
	 * 
	 * @param tableName
	 *            the new database table name of this component.
	 */
	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	/**
	 * Returns the description text associated with this component.
	 * 
	 * @return the description text associated with this component
	 */
	public String getDescription() {
		return "";
	}

	/**
	 * Returns true if this <code>FormComponent</code> is empty and false
	 * otherwise.
	 * 
	 * @return true if this <code>FormComponent</code> is empty and false
	 *         otherwise.
	 */
	public abstract boolean isEmpty();
}
